import { getParent } from '@/utils/Dom/Portal';
import classNamesFn from 'classnames';
import CSSMotion from 'rc-motion';
import KeyCode from 'rc-util/lib/KeyCode';
import React, { useEffect, useMemo, useRef } from 'react';
import DrawerPanel from './DrawerPanel';
import type { DrawerContextProps } from './context';
import DrawerContext from './context';
import { SizeEnum, TPortalParams, YmDrawerProps } from './interface';

const DrawerPopup: React.FC<YmDrawerProps & TPortalParams> = (props) => {
  const {
    prefixCls,
    scrollLocker,
    open,
    push = false,
    afterClose,
    onClose,
    maskClosable = true,
    keyboard = true,
    getContainer,
    styles,
    classNames,
    placement = 'right',
    width,
    size = 'default',
    rootClassName,
    mask = true,
    zIndex,
    forceRender,
    destroyOnClose,
    children,
    ...restProps
  } = props;
  const [animatedVisible, setAnimatedVisible] = React.useState(open);
  const drawerRef = useRef<HTMLDivElement>(null);
  const { wrapper: wrapperStyle, mask: maskStyle } = styles || {};
  const { wrapper: wrapperClassName, mask: maskClassName } = classNames || {};

  useEffect(() => {
    if (open) {
      setAnimatedVisible(true);
    }
    return () => {};
  }, [open]);

  useEffect(() => {
    if (animatedVisible) {
      drawerRef.current && drawerRef.current?.focus({ preventScroll: true }); //自动聚焦
      scrollLocker.lock();
    } else {
      scrollLocker.unLock(); //如果不是点击关闭按钮，而是外部 open = false 路径来的呢？
    }
    // return () => { };
  }, [animatedVisible, drawerRef.current]);

  // ========================= Events =========================
  function onDrawerVisibleChanged(newVisible: boolean) {
    console.log('--onDrawerVisibleChanged--newVisible---:', newVisible);
    if (newVisible) {
      //drawer打开时
    } else {
      //关闭

      // Clean up scroll bar & focus back
      setAnimatedVisible(false);

      // Trigger afterClose only when change visible from true to false
      if (animatedVisible) {
        afterClose?.();
      }
    }
  }

  function onInternalClose(e: React.SyntheticEvent) {
    onClose?.(e);
  }

  //esc键退出(只有当前dom节点/子节点 元素.focus()了，键盘事件才能触发)
  function onDrawerKeyDown(e: React.KeyboardEvent<HTMLDivElement>) {
    if (keyboard && e.keyCode === KeyCode.ESC) {
      //27
      e.stopPropagation();
      onInternalClose(e);
    }
  }

  // ============================ Push ============================
  const [pushed, setPushed] = React.useState(false);

  const parentContext = React.useContext(DrawerContext);

  // Tell parent to push
  React.useEffect(() => {
    if (open) {
      parentContext?.push?.();
    } else {
      parentContext?.pull?.();
      parentContext?.drawerDom?.focus({ preventScroll: true }); //自动聚焦
    }
  }, [open]);

  const pushDistance = useMemo<number | string>(() => {
    let currPushDistance: number | string = 180;
    if (typeof push === 'object' && push.distance) {
      currPushDistance = push.distance;
    } else if (!push) {
      currPushDistance = 0;
    }
    return currPushDistance;
  }, [push]);

  const combineWrapperStyle = useMemo<React.CSSProperties>(() => {
    const tempStyle = { ...wrapperStyle };
    if (pushDistance) {
      if (pushed) {
        const translateX =
          placement === 'right'
            ? `translateX(-${pushDistance})`
            : `translateX(${pushDistance})`;
        tempStyle.transform = translateX;
      }
    }
    if (width) {
      tempStyle.width = width;
    } else if (size) {
      tempStyle.width = SizeEnum[size];
    }
    if (zIndex) {
      tempStyle.zIndex = zIndex;
    }
    return tempStyle;
  }, [wrapperStyle, pushDistance, pushed, placement, width, size, zIndex]);

  const drawerInline = useMemo<boolean>(() => {
    let tempInline = false;
    if (getParent(getContainer) !== document.body) {
      tempInline = true;
    }
    return tempInline;
  }, [getContainer]);

  const mergedContext = React.useMemo<DrawerContextProps>(() => {
    return {
      drawerDom: drawerRef.current,
      pushDistance: pushDistance,
      push: () => {
        setPushed(true);
      },
      pull: () => {
        setPushed(false);
      },
    };
  }, [push, pushDistance, drawerRef]);

  const combineMaskStyle = useMemo(() => {
    let tempStyle = { ...maskStyle };
    if (zIndex) {
      tempStyle.zIndex = zIndex;
    }
    return tempStyle;
  }, [zIndex, maskStyle]);
  //遮罩
  const maskNode = useMemo<React.ReactNode>(
    () =>
      mask && (
        <CSSMotion
          key="mask"
          visible={open}
          motionName={`${prefixCls}-fade`}
          removeOnLeave={true}
        >
          {({ className: motionMaskClassName, style: motionMaskStyle }) => {
            return (
              <div
                className={classNamesFn(
                  `${prefixCls}-mask`,
                  motionMaskClassName,
                  maskClassName,
                  { [`${prefixCls}-mask-inline`]: drawerInline },
                )}
                style={{
                  ...motionMaskStyle,
                  ...combineMaskStyle,
                }}
                onClick={maskClosable && open ? onInternalClose : undefined}
              />
            );
          }}
        </CSSMotion>
      ),
    [maskClosable, maskClassName, maskStyle, mask, open],
  );

  //wrapper
  const wapperNode: React.ReactNode = (
    <CSSMotion
      key="panel"
      visible={open}
      motionName={`${prefixCls}-${placement}-translate`}
      forceRender={forceRender}
      onVisibleChanged={(nextVisible) => {
        onDrawerVisibleChanged(nextVisible);
      }}
      removeOnLeave={destroyOnClose}
    >
      {({ className: motionClassName, style: motionStyle }) => {
        return (
          <div
            className={classNamesFn(
              `${prefixCls}-wrapper`,
              wrapperClassName,
              motionClassName,
              { [`${prefixCls}-wrapper-inline`]: drawerInline },
            )}
            style={{
              ...motionStyle,
              ...combineWrapperStyle,
            }}
          >
            <DrawerPanel
              prefixCls={prefixCls!}
              onClose={onClose}
              classNames={classNames}
              styles={styles}
              {...restProps}
            >
              {children}
            </DrawerPanel>
          </div>
        );
      }}
    </CSSMotion>
  );

  return (
    <DrawerContext.Provider value={mergedContext}>
      <div
        className={classNamesFn(
          prefixCls,
          `${prefixCls}-${placement}`,
          rootClassName,
          {
            [`${prefixCls}-open`]: open,
          },
        )}
        tabIndex={-1}
        ref={drawerRef}
        onKeyDown={onDrawerKeyDown}
      >
        {maskNode}
        {wapperNode}
      </div>
    </DrawerContext.Provider>
  );
};

export default DrawerPopup;
